package com.quwan.security;


import com.alibaba.fastjson.JSONObject;
import com.quwan.common.exception.ExpCode;
import com.quwan.constans.AppConst;
import com.quwan.security.user.JwtUserDetails;
import com.quwan.utils.redis.Redis;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 验证Token
 *
 * @author quan
 * @date 2021-06-07 18:54
 */
@Slf4j
public class JwtTokenFilter extends OncePerRequestFilter {
    private UserDetailsService userDetailsService;
    private JwtTokenUtil jwtTokenUtil;
    private String tokenHeader;
    private String tokenHead;
    private Redis redis;

    public JwtTokenFilter(JwtProperties jwtProperties,
                          JwtTokenUtil jwtTokenUtil,
                          UserDetailsService userDetailsService, Redis redis) {
        this.tokenHeader = jwtProperties.getHeader();
        this.tokenHead = jwtProperties.getTokenHead();
        this.userDetailsService = userDetailsService;
        this.jwtTokenUtil = jwtTokenUtil;
        this.redis = redis;
    }

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
        return new AntPathMatcher().match("/auth/*/*", request.getRequestURI().replace(request.getContextPath(), ""));

    }

    @Override
    protected void doFilterInternal(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain chain) throws ServletException, IOException {

        String authHeader = request.getHeader(this.tokenHeader);
        //DONE:禁止使用URL或PostData传递JWT字符串！
        String jwtString = "";
        if (authHeader != null && authHeader.startsWith(tokenHead))
            jwtString = authHeader.replace(tokenHead + " ", "");

        if (StringUtils.isNotBlank(jwtString) && jwtString.length() > tokenHead.length() * 2) {
            boolean fail = true;
            try {
                final Claims claims = jwtTokenUtil.getClaimsFromToken(jwtString);
                if (claims != null)
                    fail = false;
            } catch (ExpiredJwtException e) {
                Claims claims = e.getClaims();
                    JSONObject json = new JSONObject();
                    json.put("code", ExpCode.TOKEN_NEED_REFRESH.getCode());
                    json.put("token", jwtTokenUtil.refreshToken(claims));
                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                    response.setCharacterEncoding("UTF-8");
                    response.getWriter().write(json.toString());
                    return;

            } catch (Exception e) {
                log.error("token解析异常 token:{},exp:{}", jwtString, e.getMessage());
            }

            if (fail) {
                JSONObject json = new JSONObject();
                json.put("code", ExpCode.LOGIN_TOKEN_EXPIRED.getCode());
                json.put("msg", ExpCode.LOGIN_TOKEN_EXPIRED.getMsg());
                response.setCharacterEncoding("UTF-8");
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                response.getWriter().write(json.toString());
                return;
            }

            //DONE: 读取Token信息，放到Request参数中
            Claims claims = jwtTokenUtil.getClaimsInfo(jwtString);
            for (String key : claims.keySet()) {
                request.setAttribute(key, claims.get(key));
            }
            String module = claims.get("module").toString();
            String username = claims.get("username") + "@@" + module;

            if ((request.getRequestURI().contains("/mgr/") && !AppConst.ADMIN_MODULE.equalsIgnoreCase(module))
                    || (request.getRequestURI().contains("/app/") && !AppConst.MEMBER_MODULE.equalsIgnoreCase(module))) {
                JSONObject json = new JSONObject();
                json.put("code", 600);
                json.put("msg", "非法操作");
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                response.setCharacterEncoding("UTF-8");
                response.getWriter().write(json.toString());
                return;
            }
            //DONE: 验证Token是否被吊销！！
            if (SecurityContextHolder.getContext().getAuthentication() == null) {
                try {
                    UsernamePasswordAuthenticationToken authentication =
                            new UsernamePasswordAuthenticationToken(
                                    null, null, null);

                    if (AppConst.ADMIN_MODULE.equalsIgnoreCase(module)) {
                        JwtUserDetails userDetails = (JwtUserDetails) this.userDetailsService.loadUserByUsername(username);
                        Object redisValue = redis.get(jwtString);
                        //  log.info("旧token:" + jwtString);
                        if (redisValue == null) {
                            log.info("token失效");
                            JSONObject json = new JSONObject();
                            json.put("code", 600);
                            json.put("msg", "非法操作");
                            response.setCharacterEncoding("UTF-8");
                            response.getWriter().write(json.toString());
                            String origin = request.getHeader("Origin");
                            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                            response.addHeader("Access-Control-Allow-Origin", origin);
                            response.addHeader("Access-Control-Allow-Credentials", "true");
                            response.addHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
                            response.addHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE,PUT");
                            response.addHeader("Access-Control-Max-Age", "3600");
                            return;
                        }
                        if (jwtTokenUtil.validateToken(jwtString, userDetails)) {
                            //做权限认证
                            authentication =
                                    new UsernamePasswordAuthenticationToken(
                                            userDetails, null, null);
                        }
                    }

                    WebAuthenticationDetails webAuthenticationDetails = new WebAuthenticationDetailsSource().buildDetails(
                            request);
                    authentication.setDetails(webAuthenticationDetails);
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                } catch (Exception e) {
                    logger.error(e);
                }
            }
        }

        chain.doFilter(request, response);
    }
}
